﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml;

namespace ResxLocalizationEditor {
    class ResxResourceWriter : ResourceWriter {

        #region Loading

        private void FetchData(Resource resource, XDocument doc) {
            var dataElements = from element in doc.Descendants("data")
                               select element;

            resource.ValueMap = new Dictionary<string, string>(dataElements.Count());
            resource.Comments = new Dictionary<string, string>();

            foreach (var element in dataElements) {
                var eleType = element.Attribute("type");

                if (eleType != null) //non-string resources have a type attribute specifying their type
                    throw new ArgumentException("Cannot parse resource file, contains non-string resource (element of " + eleType.ToString() + " type).");

                var name = element.Attribute("name").Value;

                var valueNode = element.Elements("value").SingleOrDefault();
                if (valueNode == null)
                    continue;
                resource.ValueMap.Add(name, valueNode.Value.FixNewlines());

                var commentNode = element.Elements("comment").SingleOrDefault();
                if (commentNode != null)
                    resource.Comments.Add(name, commentNode.Value.FixNewlines());
            }
        }

        private void CheckHeader(XDocument doc) {
            var headerNode = (from element in doc.Descendants("resheader")
                              where element.Attribute("name").Value.Equals("version")
                              select element.Descendants("value").First())
                              .SingleOrDefault();

            if (headerNode == null)
                throw new ArgumentException("File does not contain a <resheader> version element.");

            string headerVersion = headerNode.Value;

            if (!headerVersion.Equals("2.0"))
                throw new ArgumentException("Cannot parse resource file, version number not correct (" + headerVersion + ", expected: 2.0).");
        }

        protected override void LoadCore(Resource resource, string filePath) {
            var doc = XDocument.Load(filePath);

            CheckHeader(doc);

            FetchData(resource, doc);
        }

        #endregion

        #region Writing

        protected override void WriteCore(ResourceSet set, Resource resource, string filePath) {
            XmlWriter writer = null;
            try {
                var settings = new XmlWriterSettings {
                    CloseOutput = true,
                    Encoding = Encoding.UTF8,
                    Indent = true,
                    NewLineChars = "\n"
                };
                writer = XmlWriter.Create(filePath, settings);

                WriteResxInit(writer);

                WriteResxHeader(writer);

                WriteResxContent(resource, writer);

                WriteResxEnding(writer);
            }
            finally {
                if (writer != null)
                    writer.Close();
            }
        }

        private void WriteResxInit(XmlWriter writer) {
            writer.WriteStartElement("root");
        }

        static IDictionary<string, string> _resxHeaderElements = new Dictionary<string, string> {
			{ "resmimetype", "text/microsoft-resx" },
			{ "version", "2.0" },
			{ "reader", "System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" },
			{ "writer", "System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" }
		};

        private void WriteResxHeader(XmlWriter writer) {
            foreach (var headerPair in _resxHeaderElements) {
                writer.WriteStartElement("resheader");
                writer.WriteAttributeString("name", headerPair.Key);

                writer.WriteElementString("value", headerPair.Value);

                writer.WriteEndElement();
            }
        }

        private void WriteResxContent(Resource resource, XmlWriter writer) {
            foreach (var contentPair in resource.ValueMap) {
                writer.WriteStartElement("data");

                writer.WriteAttributeString("name", contentPair.Key);
                writer.WriteAttributeString("xml", "space", null, "preserve");
                writer.WriteElementString("value", contentPair.Value);
                if (resource.Comments.ContainsKey(contentPair.Key))
                    writer.WriteElementString("comment", resource.Comments[contentPair.Key]);

                writer.WriteEndElement();
            }
        }

        private void WriteResxEnding(XmlWriter writer) {
            writer.WriteEndElement();
        }

        #endregion

    }
}
